package com.hxy.hrm.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.hxy.hrm.domain.Course;
import com.hxy.hrm.domain.CourseType;
import com.hxy.hrm.exception.ErrorCode;
import com.hxy.hrm.exception.ValidUtil;
import com.hxy.hrm.mapper.CourseTypeMapper;
import com.hxy.hrm.service.ICourseTypeService;
import com.hxy.hrm.vo.CrumbsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author hxy
 * @since 2022-01-06
 */
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {


    @Autowired
    private CourseTypeMapper courseTypeMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    private static  final  String COURSETYPE_TREEDATA_IN_CACHE = "courseType_treeData_in_cache";//redis的key

    /**
     *
     * @param pid
     * @return
     */
    @Override
    public List<CourseType> treeData(Long pid) {
        // 方案1
        //return loadTreeDataByRecursion(pid);
        // 方案2 循环
        //加入缓存逻辑
        List<CourseType> courseTypes = (List<CourseType>) redisTemplate.opsForValue()
                .get(COURSETYPE_TREEDATA_IN_CACHE);
        if (courseTypes!=null){
            return courseTypes;
        }else{
            //500个请求进来后
            synchronized (this.getClass()){
                courseTypes = (List<CourseType>) redisTemplate.opsForValue().get(COURSETYPE_TREEDATA_IN_CACHE);
                if (courseTypes==null){
                    // （1）第一个请求会进来
                    List<CourseType> courseTypesFromDb = loadTreeDataByLood(pid);
                    // 1 缓存穿透，方案1:没有数据也放一个null（实现了），方案2：布隆过滤器
                    // 2 缓存击穿，缓存过期一瞬间来高并发。 方案1：永不过期（实现了） 方案2：单机锁（不支持集群）（实现了）
                    // @TODO 方案3：分布式锁（支持集群）
                    // 3 缓存雪崩 ，方案1：缓存永不过期，缓存预热（实现了） 方案2：让各个缓存数据过期时间不一样
                    redisTemplate.opsForValue().set(COURSETYPE_TREEDATA_IN_CACHE,courseTypesFromDb);
                    return courseTypesFromDb;
                }
                //(2)其他请求会进来
                return courseTypes;
            }
        }
    }

    /**
     *
     * @param pid
     * @return
     */
    private List<CourseType> loadTreeDataByLood(Long pid) {
        //1 定义返回列表
        List<CourseType> result = new ArrayList<>();
        //2 查询所有节点
        List<CourseType> allCourseTypes = courseTypeMapper.selectList(null);
        Map<Long,CourseType> courseTypeDto = new HashMap<>();
        allCourseTypes.forEach(courseType -> {
            courseTypeDto.put(courseType.getId(),courseType);
        });
        //3 遍历所有节点判断是否是一级节点 当前类型pid是传入pid
        allCourseTypes.forEach(courseType -> { //算法时间复杂度 10 O(n2)
            //3.1 如果是放入返回列表
            Long ppid = courseType.getPid();
            if (ppid.longValue() == pid.longValue())
                result.add(courseType);
                //3.2 如果不是，放入父亲儿子集合
            else{
                //方案2：转换为map再来获取数据 O(2n)
                CourseType parent = courseTypeDto.get(ppid);
                //2)获取到父亲儿子集合，把自己作为一个儿子放进去
                parent.getChildren().add(courseType);
            }
        });
        //4 返回“返回列表”
        return result;

    }

    /**
     * 方式1
     * 代码实现递归思想
     * @param pid
     * @return
     */
    private List<CourseType> loadTreeDataByRecursion(long pid) {
        List<CourseType> courseTypes = courseTypeMapper.selectList(new EntityWrapper<CourseType>().eq("pid", pid));
        // 出口
        if (courseTypes == null || courseTypes.size() < 1)
            return null;
        courseTypes.forEach(courseType -> {
            Long ppid = courseType.getId();
            // 自己调用自己
            List<CourseType> children = loadTreeDataByRecursion(ppid);
            courseType.setChildren(children);
        });

        return courseTypes;
    }

    /**
     *
     * @param entity
     * @return
     */
    @Override
    public boolean insert(CourseType entity) {
        //数据发生变更，清除缓存，下来查询放缓存并返回
        super.insert(entity);
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程沉睡完毕后再删除
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        return true;
    }

    /**
     *
     * @param id
     * @return
     */
    @Override
    public boolean deleteById(Serializable id) {
        super.deleteById(id);
        //数据发生变更，清 除缓存，下来查询放缓存并返回
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        return true;
    }

    /**
     *
     * @param entity
     * @return
     */
    @Override
    public boolean updateById(CourseType entity) {

        super.updateById(entity);
        //数据发生变更，清除缓存，下来查询放缓存并返回
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        return true;
    }

    /**
     *
     * @param typeId
     * @return
     */
    @Override
    public List<CrumbsVo> queryCrumbs(Long typeId) {
        // 定义返回值类型
        List<CrumbsVo> list = new ArrayList<>();
        ValidUtil.assertNotNull(typeId, ErrorCode.CODE_400_COURSE_TYPE_CRUMBS_ILLEGAL);
        // 通过typeid获取获取type再获取path
        String path = courseTypeMapper.selectById(typeId).getPath();
        //split分隔
        String[] typeIds = path.split("\\.");
        //遍历每个节点构造CrumbsVo放入list
        for (String idStr : typeIds) {
            CrumbsVo crumbsVo = new CrumbsVo();
            Long id = Long.valueOf(idStr);
            CourseType owner = courseTypeMapper.selectById(id);
            crumbsVo.setOwnerCourseType(owner);
            //通过自己的pid获取所有的下级
            Long pid = owner.getPid();
            List<CourseType> otherCourseTypes = courseTypeMapper.selectList(new EntityWrapper<CourseType>().eq("pid", pid));
            Iterator<CourseType> iterator = otherCourseTypes.iterator();
            while (iterator.hasNext()){
                CourseType courseType = iterator.next();
                if (id.longValue()==courseType.getId().longValue()){
                    iterator.remove();
                    break;
                }
            }
            crumbsVo.setOtherCourseTypes(otherCourseTypes);
            list.add(crumbsVo);
        }
        return list;
    }
}
